from typing import Annotated, Optional
import re

from fastapi import Query, Body
from pydantic import BaseModel, model_validator

from app.utils import regexes, types
from app.models.common import AppId, Pagination, CardTypeId, CardId
from app.models import responses


def parse_time_range(value: Optional[dict]):
    value = types.safe_loads_json(value)
    if not value:
        return None

    start = value.get('start', '')
    end = value.get('end', '')

    if not re.match(regexes.TIMESTAMP, str(start)):
        raise responses.FailureResponseError(-102, msg='起始时间格式错误')

    if not re.match(regexes.TIMESTAMP, str(end)):
        raise responses.FailureResponseError(-102, msg='结束时间格式错误')

    if int(end) < int(start):
        raise responses.FailureResponseError(-102, msg='结束时间不能早于起始时间')

    return {
        'start': int(start),
        'end': int(end)
    }


class AppCardList(AppId, Pagination):
    """分页获取指定应用的卡密列表"""
    status: Optional[int]
    card: str
    card_type: str
    remark: str
    device_code: str
    create_times: Optional[dict]
    activate_times: Optional[dict]
    expire_times: Optional[dict]

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        data['status'] = types.safe_int(data.get('status', None), None)
        data['card'] = data.get('card', '')
        data['card_type'] = data.get('card_type', '')
        data['remark'] = data.get('remark', '')
        data['device_code'] = data.get('device_code', '')
        data['create_times'] = parse_time_range(data.get('create_times', None))
        data['activate_times'] = parse_time_range(data.get('activate_times', None))
        data['expire_times'] = parse_time_range(data.get('expire_times', None))
        return data

    @model_validator(mode='after')
    def after(self):
        if self.status is not None and not (0 <= self.status <= 255):
            raise responses.FailureResponseError(-102, msg='卡密状态值应介于 0-255 之间')

        if len(self.card) > 64:
            raise responses.FailureResponseError(-102, msg='卡密的长度不能超过 64 位')

        if len(self.card_type) > 64:
            raise responses.FailureResponseError(-102, msg='卡种名称长度不能超过 64 位')

        if len(self.remark) > 512:
            raise responses.FailureResponseError(-102, msg='卡密的备注长度不能超过 512 个字符')

        if len(self.device_code) > 128:
            raise responses.FailureResponseError(-102, msg='设备码长度不能超过 128 位')

        return self


AppCardListParams = Annotated[AppCardList, Query()]


class CreateCard(CardTypeId):
    card_count: int
    use_app_config: bool
    max_bind: int
    time_restriction: str
    unbind_enabled: bool
    unbind_cost: int
    remark: str
    extra_data: dict

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        data['card_count'] = types.safe_int(data.get('card_count', None), 0)
        data['use_app_config'] = data.get('use_app_config', True)
        data['max_bind'] = types.safe_int(data.get('max_bind', None), 1)
        data['time_restriction'] = data.get('time_restriction', '00:00:00-23:59:59')
        data['unbind_enabled'] = data.get('unbind_enabled', False)
        data['unbind_cost'] = types.safe_int(data.get('unbind_cost', None), 0)
        data['remark'] = data.get('remark', '')
        data['extra_data'] = types.safe_dict(data.get('extra_data', None), {})
        return data

    @model_validator(mode='after')
    def after(self):
        if not (1 <= self.card_count <= 1000):
            raise responses.FailureResponseError(-102, msg='卡密数量应介于 1-1000 张之间')

        if not (1 <= self.max_bind <= 255):
            raise responses.FailureResponseError(-102, msg='卡密绑定设备的上下限为：1-255')

        if not re.fullmatch(regexes.TIMERANGE, self.time_restriction):
            raise responses.FailureResponseError(-102, msg='登录限制时段格式参考 00:00:00-23:59:59')

        if not (0 <= self.unbind_cost <= 65535):
            raise responses.FailureResponseError(-102, msg='解绑扣时时长应介于 0-65535 之间')

        if len(self.remark) > 512:
            raise responses.FailureResponseError(-102, msg='卡密的备注长度不能超过 512 个字符')

        if len(str(self.extra_data)) > 1024:
            raise responses.FailureResponseError(-102, msg='额外数据长度不能超过 1024 个字符')

        return self


CreateCardJSON = Annotated[CreateCard, Body()]


class UpdateCard(CardId):
    use_app_config: Optional[bool]
    max_bind: Optional[int]
    time_restriction: Optional[str]
    unbind_enabled: Optional[bool]
    unbind_cost: Optional[int]
    remark: Optional[str]
    extra_data: Optional[dict]

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        data['use_app_config'] = data.get('use_app_config', None)
        data['max_bind'] = types.safe_int(data.get('max_bind', None), None)
        data['time_restriction'] = data.get('time_restriction', None)
        data['unbind_enabled'] = data.get('unbind_enabled', None)
        data['unbind_cost'] = types.safe_int(data.get('unbind_cost', None), None)
        data['remark'] = data.get('remark', None)
        data['extra_data'] = types.safe_loads_json(data.get('extra_data')) or None
        return data

    @model_validator(mode='after')
    def after(self):
        if self.max_bind is not None and not (1 <= self.max_bind <= 255):
            raise responses.FailureResponseError(-102, msg='卡密绑定设备的上下限为：1-255')

        if self.time_restriction is not None and not re.fullmatch(regexes.TIMERANGE, self.time_restriction):
            raise responses.FailureResponseError(-102, msg='登录限制时段格式参考 00:00:00-23:59:59')

        if self.unbind_cost is not None and not (0 <= self.unbind_cost <= 65535):
            raise responses.FailureResponseError(-102, msg='解绑扣时时长应介于 0-65535 之间')

        if self.remark is not None and len(self.remark) > 512:
            raise responses.FailureResponseError(-102, msg='卡密的备注长度不能超过 512 个字符')

        if self.extra_data is not None and len(str(self.extra_data)) > 1024:
            raise responses.FailureResponseError(-102, msg='额外数据长度不能超过 1024 个字符')

        return self


UpdateCardJSON = Annotated[UpdateCard, Body()]


class FreezeCard(CardId):
    is_freeze: bool = True


FreezeCardJSON = Annotated[FreezeCard, Body()]


class BatchAppCardIds(AppId):
    card_ids: list[int] = []

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        card_ids = []
        for raw_card_id in list(data.get('card_ids', [])):
            card_id = types.safe_int(raw_card_id, None)
            if card_id is not None:
                card_ids.append(card_id)

        data['card_ids'] = card_ids
        return data

    @model_validator(mode='after')
    def after(self):
        if type(self.card_ids) is not list:
            raise responses.FailureResponseError(-102, msg='多个卡密 ID 必须是列表类型')

        if len(self.card_ids) == 0:
            raise responses.FailureResponseError(-102, msg='请传入至少 1 个卡密 ID')

        return self


BatchDeleteAppCardsJSON = Annotated[BatchAppCardIds, Body()]


class IncreaseCardHour(AppId):
    card_ids: list[int] = []
    increase_hour: int

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        data['increase_hour'] = types.safe_int(data.get('increase_hour', None), None)
        card_ids = []
        for raw_card_id in list(data.get('card_ids', [])):
            card_id = types.safe_int(raw_card_id, None)
            if card_id is not None:
                card_ids.append(card_id)

        data['card_ids'] = card_ids
        return data

    @model_validator(mode='after')
    def after(self):
        if self.increase_hour is None:
            raise responses.FailureResponseError(-102, msg='时长必须是有效的数字')

        if not (1 <= self.increase_hour <= 8064):
            raise responses.FailureResponseError(-102, msg='增加的时长范围应在 1-8064')

        if type(self.card_ids) is not list:
            raise responses.FailureResponseError(-102, msg='多个卡密 ID 必须是列表类型')

        if len(self.card_ids) == 0:
            raise responses.FailureResponseError(-102, msg='请传入至少 1 个卡密 ID')

        return self


IncreaseCardsHourJSON = Annotated[IncreaseCardHour, Body()]
